﻿using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml;
using AvalonDock;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using Microsoft.Win32;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FxArchitect.Xna_3_1
{
    public class HLSLDocumentContent : DocumentContent
    {
        TextEditor textEditor;
        FoldingManager foldingManager;
        HLSLFoldingStrategy foldingStrategy;
        GraphicsResourceManager graphicsManager;
        TreeViewItem treeViewItem;
        Button btnTexture;
        ComboBox cbTechnique, cbSamplerIndex,
                 cbMinFilter, cbMagFilter, cbMipFilter,
                 cbAddressU, cbAddressV, cbAddressW;
        string includeRoot;

        public TextEditor TextEditor { get { return textEditor; } }
        public TreeViewItem TreeViewItem { get { return treeViewItem; } }
        public GraphicsResourceManager GraphicsManager { get { return graphicsManager; } }
        public string IncludeRoot { get { return includeRoot; } }

        public new string Title
        {
            get { return base.Title; }
            set { treeViewItem.Header = base.Title = value; }
        }

        public HLSLDocumentContent(Game game, string filename, string shaderCode)
            : base()
        {
            graphicsManager = new GraphicsResourceManager(game);
            try { graphicsManager.Initialize(); }
            catch { }
            graphicsManager.EffectCreated += new Action<Effect>(OnEffectCreated);

            textEditor = new TextEditor();
            textEditor.Foreground = Brushes.White;
            textEditor.Background = new SolidColorBrush(new System.Windows.Media.Color() { R = 0x20, G = 0x20, B = 0x20, A = 0xFF });
            textEditor.HorizontalAlignment = HorizontalAlignment.Stretch;
            textEditor.VerticalAlignment = VerticalAlignment.Stretch;
            textEditor.Document.Text = shaderCode ?? string.Empty;
            textEditor.TextArea.TextEntered += new TextCompositionEventHandler(TextArea_TextEntered);

            InitializeSyntaxHighlighting();
            InitializeFolding();
            InitializeTreeViewItem();

            Title = filename;
            Content = textEditor;
        }

        void InitializeSyntaxHighlighting()
        {
            // read in the syntax highlighting ruleset
            using (Stream stream = new MemoryStream(FxResources.hlsl_Direct3D9))
            {
                using (XmlReader reader = new XmlTextReader(stream))
                {
                    textEditor.SyntaxHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance);
                }
            }
        }
        void InitializeFolding()
        {
            foldingManager = FoldingManager.Install(textEditor.TextArea);
            foldingStrategy = new HLSLFoldingStrategy();

            Dispatcher.BeginInvoke(new Action<FoldingManager, TextDocument>(foldingStrategy.UpdateFoldings),
                                   foldingManager, textEditor.Document);
        }
        void InitializeTreeViewItem()
        {
            treeViewItem = new TreeViewItem();
            TreeViewItem.HorizontalAlignment = HorizontalAlignment.Stretch;

            Button btnIncludeRoot = new Button();
            btnIncludeRoot.HorizontalAlignment = HorizontalAlignment.Stretch;
            btnIncludeRoot.Click += (sender, e) =>
            {
                OpenFileDialog openDialog = new OpenFileDialog();
                if (openDialog.ShowDialog() ?? false)
                {
                    FileInfo fileInfo = new FileInfo(openDialog.FileName);
                    btnIncludeRoot.Content = fileInfo.Directory.FullName;
                    includeRoot = fileInfo.Directory.FullName;
                }
            };

            StackPanel spIncludeRoot = new StackPanel();
            spIncludeRoot.Orientation = Orientation.Horizontal;
            spIncludeRoot.HorizontalAlignment = HorizontalAlignment.Stretch;
            spIncludeRoot.Children.Add(new Label() { Content = "Root " });
            spIncludeRoot.Children.Add(btnIncludeRoot);
            treeViewItem.Items.Add(new TreeViewItem() { Header = spIncludeRoot });

            treeViewItem.Items.Add(CreateRenderStateTVI());
        }

        TreeViewItem CreateRenderStateTVI()
        {
            cbTechnique = new ComboBox();
            cbTechnique.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbTechnique.SelectionChanged += (sender, e) =>
            {
                graphicsManager.ActiveTechniqueName = cbTechnique.SelectedItem as string;
            };

            cbSamplerIndex = new ComboBox();
            cbSamplerIndex.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbSamplerIndex.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                cbAddressU.SelectedIndex = (int)state.AddressU - 1;
                cbAddressV.SelectedIndex = (int)state.AddressV - 1;
                cbAddressW.SelectedIndex = (int)state.AddressW - 1;
                cbMagFilter.SelectedIndex = (int)state.MagFilter - ((int)state.MagFilter > 3 ? 2 : 0);
                cbMinFilter.SelectedIndex = (int)state.MinFilter - ((int)state.MinFilter > 3 ? 2 : 0);
                cbMipFilter.SelectedIndex = (int)state.MipFilter - ((int)state.MipFilter > 3 ? 2 : 0);

                Texture2D texture = graphicsManager.GetTexturePS(cbSamplerIndex.SelectedIndex);
                btnTexture.Content = (texture != null) ? texture.Name : "None";
            };

            cbMinFilter = new ComboBox();
            cbMinFilter.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbMinFilter.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                state.MinFilter = (TextureFilter)(cbMinFilter.SelectedIndex + (cbMinFilter.SelectedIndex > 3 ? 2 : 0));
                graphicsManager.SetSamplerStatePS(state, cbSamplerIndex.SelectedIndex);
            };

            cbMagFilter = new ComboBox();
            cbMagFilter.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbMagFilter.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                state.MagFilter = (TextureFilter)(cbMagFilter.SelectedIndex + (cbMagFilter.SelectedIndex > 3 ? 2 : 0));
                graphicsManager.SetSamplerStatePS(state, cbSamplerIndex.SelectedIndex);
            };

            cbMipFilter = new ComboBox();
            cbMipFilter.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbMipFilter.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                state.MipFilter = (TextureFilter)(cbMipFilter.SelectedIndex + (cbMipFilter.SelectedIndex > 3 ? 2 : 0));
                graphicsManager.SetSamplerStatePS(state, cbSamplerIndex.SelectedIndex);
            };

            cbAddressU = new ComboBox();
            cbAddressU.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbAddressU.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                state.AddressU = (TextureAddressMode)(cbAddressU.SelectedIndex + 1);
                graphicsManager.SetSamplerStatePS(state, cbSamplerIndex.SelectedIndex);
            };

            cbAddressV = new ComboBox();
            cbAddressV.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbAddressV.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                state.AddressV = (TextureAddressMode)(cbAddressV.SelectedIndex + 1);
                graphicsManager.SetSamplerStatePS(state, cbSamplerIndex.SelectedIndex);
            };

            cbAddressW = new ComboBox();
            cbAddressW.HorizontalAlignment = HorizontalAlignment.Stretch;
            cbAddressW.SelectionChanged += (sender, e) =>
            {
                SamplerStateBlock state = graphicsManager.GetSamplerStatePS(cbSamplerIndex.SelectedIndex);
                state.AddressW = (TextureAddressMode)(cbAddressW.SelectedIndex + 1);
                graphicsManager.SetSamplerStatePS(state, cbSamplerIndex.SelectedIndex);
            };

            btnTexture = new Button();
            btnTexture.HorizontalAlignment = HorizontalAlignment.Stretch;
            btnTexture.Click += (sender, e) =>
            {
                OpenFileDialog openDialog = new OpenFileDialog();
                openDialog.CheckFileExists = true;
                openDialog.Filter = FxResources.Filter_TextureFile;

                if (openDialog.ShowDialog() ?? false)
                {
                    graphicsManager.LoadTexture(openDialog.FileName, cbSamplerIndex.SelectedIndex);
                    btnTexture.Content = openDialog.FileName;
                }
            };

            Array texFilterValues = Enum.GetValues(typeof(TextureFilter));
            foreach (object texFilter in texFilterValues)
            {
                cbMagFilter.Items.Add(texFilter);
                cbMinFilter.Items.Add(texFilter);
                cbMipFilter.Items.Add(texFilter);
            }

            Array texAddressValues = Enum.GetValues(typeof(TextureAddressMode));
            foreach (object texAddress in texAddressValues)
            {
                cbAddressU.Items.Add(texAddress);
                cbAddressV.Items.Add(texAddress);
                cbAddressW.Items.Add(texAddress);
            }

            int maxSamplers = graphicsManager.SamplerStateCount;
            for (int i = 0; i < maxSamplers; i++)
            {
                cbSamplerIndex.Items.Add(string.Format("register(ps, s{0})", i));

                if (i == cbSamplerIndex.SelectedIndex)
                {
                    SamplerStateBlock state = graphicsManager.GetSamplerStatePS(i);
                    cbAddressU.SelectedIndex = (int)state.AddressU - 1;
                    cbAddressV.SelectedIndex = (int)state.AddressV - 1;
                    cbAddressW.SelectedIndex = (int)state.AddressW - 1;
                    cbMagFilter.SelectedIndex = (int)state.MagFilter - ((int)state.MagFilter > 3 ? 2 : 0);
                    cbMinFilter.SelectedIndex = (int)state.MinFilter - ((int)state.MinFilter > 3 ? 2 : 0);
                    cbMipFilter.SelectedIndex = (int)state.MipFilter - ((int)state.MipFilter > 3 ? 2 : 0);
                }
            }
            cbSamplerIndex.SelectedIndex = 0;

            StackPanel stack = new StackPanel();
            stack.Orientation = Orientation.Vertical;
            stack.HorizontalAlignment = HorizontalAlignment.Stretch;

            StackPanel spTechnique = new StackPanel();
            spTechnique.Orientation = Orientation.Horizontal;
            spTechnique.HorizontalAlignment = HorizontalAlignment.Stretch;
            spTechnique.Children.Add(new Label() { Content = "Techique " });
            spTechnique.Children.Add(cbTechnique);
            stack.Children.Add(spTechnique);

            StackPanel spSamplerIndex = new StackPanel();
            spSamplerIndex.Orientation = Orientation.Horizontal;
            spSamplerIndex.HorizontalAlignment = HorizontalAlignment.Stretch;
            spSamplerIndex.Children.Add(new Label() { Content = "Sampler " });
            spSamplerIndex.Children.Add(cbSamplerIndex);
            stack.Children.Add(spSamplerIndex);

            StackPanel spMinFilter = new StackPanel();
            spMinFilter.Orientation = Orientation.Horizontal;
            spMinFilter.HorizontalAlignment = HorizontalAlignment.Stretch;
            spMinFilter.Children.Add(new Label() { Content = "Min Filter " });
            spMinFilter.Children.Add(cbMinFilter);
            stack.Children.Add(spMinFilter);

            StackPanel spMagFilter = new StackPanel();
            spMagFilter.Orientation = Orientation.Horizontal;
            spMagFilter.HorizontalAlignment = HorizontalAlignment.Stretch;
            spMagFilter.Children.Add(new Label() { Content = "Mag Filter " });
            spMagFilter.Children.Add(cbMagFilter);
            stack.Children.Add(spMagFilter);

            StackPanel spMipFilter = new StackPanel();
            spMipFilter.Orientation = Orientation.Horizontal;
            spMipFilter.HorizontalAlignment = HorizontalAlignment.Stretch;
            spMipFilter.Children.Add(new Label() { Content = "Mip Filter " });
            spMipFilter.Children.Add(cbMipFilter);
            stack.Children.Add(spMipFilter);

            StackPanel spAddressU = new StackPanel();
            spAddressU.Orientation = Orientation.Horizontal;
            spAddressU.HorizontalAlignment = HorizontalAlignment.Stretch;
            spAddressU.Children.Add(new Label() { Content = "Address U " });
            spAddressU.Children.Add(cbAddressU);
            stack.Children.Add(spAddressU);

            StackPanel spAddressV = new StackPanel();
            spAddressV.Orientation = Orientation.Horizontal;
            spAddressV.HorizontalAlignment = HorizontalAlignment.Stretch;
            spAddressV.Children.Add(new Label() { Content = "Address V " });
            spAddressV.Children.Add(cbAddressV);
            stack.Children.Add(spAddressV);

            StackPanel spAddressW = new StackPanel();
            spAddressW.Orientation = Orientation.Horizontal;
            spAddressW.HorizontalAlignment = HorizontalAlignment.Stretch;
            spAddressW.Children.Add(new Label() { Content = "Address W " });
            spAddressW.Children.Add(cbAddressW);
            stack.Children.Add(spAddressW);

            StackPanel spTexture = new StackPanel();
            spTexture.Orientation = Orientation.Horizontal;
            spTexture.HorizontalAlignment = HorizontalAlignment.Stretch;
            spTexture.Children.Add(new Label() { Content = "Texture " });
            spTexture.Children.Add(btnTexture);
            stack.Children.Add(spTexture);

            TreeViewItem treeViewItem = new TreeViewItem()
            {
                HorizontalAlignment = HorizontalAlignment.Stretch,
                Header = "Samplers"
            };
            treeViewItem.Items.Add(new TreeViewItem()
            {
                HorizontalAlignment = HorizontalAlignment.Stretch,
                Header = stack
            });
            return treeViewItem;
        }

        void OnEffectCreated(Effect effect)
        {
            string previousTechnique = graphicsManager.ActiveTechniqueName;
            string[] techniques = new string[effect.Techniques.Count];
            int techniqueIndex = 0;
            for (int i = 0; i < effect.Techniques.Count; i++)
            {
                techniques[i] = effect.Techniques[i].Name;
                if (techniques[i] == previousTechnique)
                    techniqueIndex = i;
            }
            graphicsManager.ActiveTechniqueName = techniques[techniqueIndex];

            Dispatcher.BeginInvoke(new Action(() =>
            {
                cbTechnique.Items.Clear();
                for (int i = 0; i < techniques.Length; i++)
                    cbTechnique.Items.Add(techniques[i]);
                cbTechnique.SelectedIndex = techniqueIndex;
            }), null);
        }

        void TextArea_TextEntered(object sender, TextCompositionEventArgs e)
        {
            Dispatcher.BeginInvoke(new Action<FoldingManager, TextDocument>(foldingStrategy.UpdateFoldings),
                                   foldingManager, textEditor.Document);
        }
    }
}
